﻿using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Xml;
using repliback4.Net.Protocol;
using repliback4.Net.Protocol.Messages;
using repliback4.Net.Protocol.States;

using repliback4.XmlStreaming;
using Tools.Debugging;
using repliback4.Net.Identification;
using repliback4.Net.Protocol.States.ServerSide;
using repliback4.Misc.Queue;

namespace repliback4.Net.ServerSide {

    public class ClientInfo {
        public Rid Rid;
        public Boolean Authenticated = false;
        public Boolean Encrypted = false;

        public override string ToString ( ) {
            return String.Format( "{0} A:{1}E:{2}", this.Rid, this.Authenticated, this.Encrypted );
        }
        public static implicit operator string ( ClientInfo info ) {
            return info.ToString( );
        }
    }


    public class Client : SideEndpoint, IDisposable {

        internal ServerConnection serverConection;
        protected Server server;
        public Server Server { get { return this.server; } }


        internal ClientInfo ClientInfo = new ClientInfo( );


        public Client ( Server server, TcpClient connection ) {
            Logger.Created( this );
            this.server = server;

            this.queue = new QueueManager<InfoQuery, QueueDirection>( );

            this.queue.CreateQueue( QueueDirection.In );
            this.queue.AddOnItemInQueueHandler( QueueDirection.In, new AutoQueue<InfoQuery>.OnItemInQueueHandler( this.queueIn_OnDeque ) );

            this.queue.CreateQueue( QueueDirection.Out );
            this.queue.AddOnItemInQueueHandler( QueueDirection.Out, new AutoQueue<InfoQuery>.OnItemInQueueHandler( this.queueOut_OnDeque ) );

            this.serverConection = new ServerConnection( this, connection );
            this.serverConection.OnReceive += new AsyncConnection.OnReceiveHandler( this.serverConection_OnReceive );
            this.serverConection.OnDisconnect += new AsyncConnection.OnDisconnectHandler( this.serverConection_OnDisconnect );
            this.serverConection.OnRemoteDisconnect += new AsyncConnection.OnRemoteDisconnectHandler( this.serverConection_OnRemoteDisconnect );
            this.serverConection.OnConnect += new AsyncConnection.OnConnectHandler( this.serverConection_OnConnect );

            this.parser.OnXmlDeclaration += new MessageParser.OnXmlDeclarationHandler( this.parser_OnXmlDeclaration );
            this.parser.OnStreamError += new MessageParser.OnStreamErrorHandler( this.parser_OnStreamError );
            this.parser.OnStreamBegin += new MessageParser.OnStreamBeginHandler( this.parser_OnStreamBegin );
            this.parser.OnStreamEnd += new MessageParser.OnStreamEndHandler( parser_OnStreamEnd );
            this.parser.OnTag += new MessageParser.OnTagHandler( this.parser_OnTag );

            this.stateManager = new ServerStateManager( this );
            this.stateManager.CurrentState = new DisconnectedState( this.stateManager );

            this.writer = new XmlStreamWriter( );

            this.serverConection.Start( );
        }

        ~Client ( ) {
            //Logger.Debug( this, "destructing" );
            // this.Finalize( );
        }

        #region :: Queue Event handlers

        protected void queueOut_OnDeque ( InfoQuery iq ) {
            Logger.Debug( this, "sending iq" );
            this.writer.WriteStanza( iq );
            this.writer.Flush( );
        }
        protected void queueIn_OnDeque ( InfoQuery iq ) {

            this.Server.Queue.Enque( ServerQueueDirection.FromClient, new QueryFromClient( ) { Query = iq, Sender = this } );
        }

        #endregion


        #region :: Connection Event handlers ::

        public void serverConection_OnReceive ( Response response ) {
            Logger.Debug( this, "received: {0}", response.Data );
            try {
                this.parser.Parse( response.Data );
            } catch ( XmlException e ) {
                Logger.Exception( this, "serverConection_OnReceive: XmlException", e );
            } catch ( Exception e ) {
                Logger.Exception( this, "serverConection_OnReceive: Exception", e );
            }
        }
        public void serverConection_OnDisconnect ( DisconnectData data ) {
            Logger.Debug( this, "fired serverConection_OnDisconnect" );

        }
        public void serverConection_OnRemoteDisconnect ( RemoteDisconnectData data ) {
            Logger.Debug( this, "fired serverConection_OnRemoteDisconnect" );
            this.Server.RemoteDisconnected( this );
        }
        public void serverConection_OnConnect ( ConnectData data ) {
            Logger.Debug( this, "fired serverConection_OnConnect, binding xmlwriter to socket" );
            this.writer.Writer = data.connection.StreamWriter;

        }

        #endregion


        public void Start ( ) {
            //throw new NotImplementedException( );
        }

        #region IDisposable Members

        public void Dispose ( ) {
            this.queue.Dispose( );
            this.serverConection.Dispose( );
            //this.stateManager.Dispose( );
            //this.writer.Dispose( );

        }

        #endregion


    }
}
